<!--
 * Copyright 2011, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>WebGL Color Adjust</title>
<style>
html, body {
  width: 100%;
  height: 100%;
  border: 0px;
  padding: 0px;
  margin: 0px;
  background-color: dark-red;
  font-family: sans-serif;
  overflow: hidden;
  color: #fff;
}
a {
 color: #fff;
}
#info {
    font-size: small;
    position: absolute;
    top: 0px; width: 100%;
    padding: 5px;
    text-align: center;
    z-index: 2;
}
CANVAS {
  background-color: gray;
  display: block;
}
#advanced {
  color: gray;
}
#uiContainer {
  position: absolute;
  top: 10px;
  left: 20px;
  z-index: 2;
  color: white;
  background-color: rgba(0,0,0,0.5);
  border-radius: 10px;
  padding: 10px;
  font-size: small;
}
.clickable {
  cursor: pointer;
}
#viewContainer {
  width: 100%;
  height: 100%;
}
img {
  border: solid black 2px;
}
      .fulldialog {
        display: flex;
        display: -webkit-flex;
        flex-flow: column;
        -webkit-flex-flow: column;
        justify-content: center;
        align-content: center;
        align-items: center;
        -webkit-justify-content: center;
        -webkit-align-content: center;
        -webkit-align-items: center;

        position: absolute;
        z-index: 10;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0,0,0,0.8);
      }
      .fulldialog > * {
        padding: 1em;
        background-color: rgba(0,0,0,0.8);
        border: 1px solid #666;
        border-radius: 0.5em;
      }
#start {
  background-color: rgba(0,0,0,0.0);
}
#start>div {
  text-align: center;
}
#start>div>div {
  text-align: left;
  color: red;
  font-weight: bold;
}
#start img {
  display: inline-block;
  width: 10em;
  height: 10em;
}
#start:after {
 box-shadow: inset 0 0 10em rgba(0,0,0,0.9);
 position: absolute;
 top: 0;
 left: 0;
 width: 100%;
 height: 100%;
 z-index: 12;
 content: "";
}
</style>
</head>
<body>
<div id="info">
  <div><a href="http://threedlibrary.googlecode.com" target="_blank">tdl.js</a> - color adjust</div>
  <div><a href="http://www.youtube.com/watch?v=rfQ8rKGTVlg#t=25m03s">click here to see how it works</a></div>
</div>
<div id="uiContainer">
  <div>Adjustment</div>
  <div id="adjustments"></div>
</div>
<div id="viewContainer">
<canvas id="canvas" width="1024" height="1024" style="width: 100%; height: 100%;"></canvas>
</div>
<div id="start" class="fulldialog">
    <div>
      <img src="../video/start.svg" />
    </div>
</div>
</body>
<!-- ===[ basic ]============================================== -->
<script id="basicVertexShader" type="text/something-not-javascript">
uniform mat4 worldViewProjection;
attribute vec4 position;
attribute vec2 texCoord;
varying vec2 v_texCoord;
void main() {
  gl_Position = worldViewProjection * position;
  v_texCoord = texCoord;
}
</script>
<script id="basicFragmentShader" type="text/something-not-javascript">
#ifdef GL_FRAGMENT_PRECISION_HIGH
  precision highp float;
#else
  precision mediump float;
#endif

uniform sampler2D texture;
varying vec2 v_texCoord;
void main() {
  gl_FragColor = texture2D(texture, v_texCoord);
}
</script>
<!-- ===[ colorAdjust ]============================================== -->
<script id="colorAdjustVertexShader" type="text/something-not-javascript">
attribute vec4 position;
attribute vec2 texCoord;
varying vec2 v_texCoord;
void main() {
  gl_Position = position;
  v_texCoord = texCoord;
}
</script>
<script id="colorAdjustFragmentShader" type="text/something-not-javascript">
precision mediump float;
uniform float mixAmount;
uniform sampler2D inTexture;
uniform sampler2D colorCube0;
uniform sampler2D colorCube1;
varying vec2 v_texCoord;

vec4 sampleAs3DTexture(sampler2D tex, vec3 texCoord, float size) {
  float sliceSize = 1.0 / size;                  // space of 1 slice
  float slicePixelSize = sliceSize / size;       // space of 1 pixel
  float width = size - 1.0;
  float sliceInnerSize = slicePixelSize * width; // space of size pixels
  float zSlice0 = floor( texCoord.z * width);
  float zSlice1 = min( zSlice0 + 1.0, width);
  float xOffset = slicePixelSize * 0.5 + texCoord.x * sliceInnerSize;
  float yRange = (texCoord.y * width + 0.5) / size;
  float s0 = xOffset + (zSlice0 * sliceSize);
  #if defined(USE_NEAREST)
    return texture2D(tex, vec2( s0, yRange));
  #else
    float s1 = xOffset + (zSlice1 * sliceSize);
    vec4 slice0Color = texture2D(tex, vec2(s0, yRange));
    vec4 slice1Color = texture2D(tex, vec2(s1, yRange));
    float zOffset = mod(texCoord.z * width, 1.0);
    return mix(slice0Color, slice1Color, zOffset);
  #endif
}

void main() {
  vec4 originalColor = texture2D(inTexture, v_texCoord);
  vec4 color0 = sampleAs3DTexture(colorCube0, originalColor.rgb, 8.0);
  vec4 color1 = sampleAs3DTexture(colorCube1, originalColor.rgb, 8.0);
  gl_FragColor = vec4(mix(color0, color1, mixAmount).rgb, originalColor.a);
}
</script>
<script src="../tdl/base.js"></script>
<script>
tdl.require('tdl.buffers');
tdl.require('tdl.fast');
tdl.require('tdl.framebuffers');
tdl.require('tdl.log');
tdl.require('tdl.math');
tdl.require('tdl.models');
tdl.require('tdl.particles');
tdl.require('tdl.primitives');
tdl.require('tdl.programs');
tdl.require('tdl.textures');
tdl.require('tdl.webgl');

var isiOS = window.navigator.userAgent.match(/iPhone|iPad|iPod/i);

// globals
var gl;                   // the gl context.
var canvas;               // the canvas
var math;                 // the math lib.
var fast;                 // the fast math lib.
var g_logGLCalls = true;  // whether or not to log webgl calls
var g_debug = false;      // whether or not to debug.
var g_drawOnce = false;   // draw just one frame.
var g_setCountElements = [];
var g_autoSet = true;
var g_autoSetting = 0;
var g_useNearest = false;
var g_videoTexture;
var g_colorCube0Texture;
var g_colorCube1Texture;
var g_oldIndex = 0;
var g_mixTimer = 0;
var g_mixAmount = 0;
var g_eyeClock = 0;

var g_adjustmentNames;
var g_mixDuration = 1;
var g_applyColorAdjust = true;
var g_targetHeight = 0.0;
var g_targetRadius = 1;
var g_eyeHeight    = 0;
var g_eyeRadius    = 1.8;
var g_eyeSpeed     = 0.0;

//g_drawOnce = true;
//g_debug = true;

var g_adjustmentImages = [
  { name: 'default',         nearest: false, url: "adjustments/default.png" },
  { name: 'monochrome',      nearest: false, url: "adjustments/monochrome.png" },
  { name: 'sepia',           nearest: false, url: "adjustments/sepia.png" },
  { name: 'saturated',       nearest: false, url: "adjustments/saturated.png", },
  { name: 'posterize',       nearest: true , url: "adjustments/posterize.png", },
  { name: 'posterize-3-rgb', nearest: true , url: "adjustments/posterize-3-rgb.png", },
  { name: 'posterize-3-lab', nearest: true , url: "adjustments/posterize-3-lab.png", },
  { name: 'posterize-4-lab', nearest: true , url: "adjustments/posterize-4-lab.png", },
  { name: 'posterize-more',  nearest: true , url: "adjustments/posterize-more.png", },
  { name: 'inverse',         nearest: false, url: "adjustments/inverse.png", },
  { name: 'color negative',  nearest: false, url: "adjustments/color-negative.png", },
  { name: 'high contrast',   nearest: false, url: "adjustments/high-contrast-bw.png", },
  { name: 'funky contrast',  nearest: false, url: "adjustments/funky-contrast.png", },
  { name: 'nightvision',     nearest: false, url: "adjustments/nightvision.png", },
  { name: 'thermal',         nearest: false, url: "adjustments/thermal.png", },
  { name: 'b/w',             nearest: true , url: "adjustments/black-white.png", },
  { name: 'hue +60',         nearest: false, url: "adjustments/hue-plus-60.png", },
  { name: 'hue +180',        nearest: false, url: "adjustments/hue-plus-180.png", },
  { name: 'hue -60',         nearest: false, url: "adjustments/hue-minus-60.png", },
  { name: 'red to cyan',     nearest: false, url: "adjustments/red-to-cyan.png" },
  { name: 'blues',           nearest: false, url: "adjustments/blues.png" },
  { name: 'infrared',        nearest: false, url: "adjustments/infrared.png" },
  { name: 'radioactive',     nearest: false, url: "adjustments/radioactive.png" },
  { name: 'goolgey',         nearest: false, url: "adjustments/googley.png" },
  { name: 'bgy',             nearest: false, url: "adjustments/bgy.png" }
];

function ValidateNoneOfTheArgsAreUndefined(functionName, args) {
  for (var ii = 0; ii < args.length; ++ii) {
    if (args[ii] === undefined) {
      tdl.error("undefined passed to gl." + functionName + "(" +
          tdl.webgl.glFunctionArgsToString(functionName, args) + ")");
    }
  }
}

function Log(msg) {
  if (g_logGLCalls) {
    tdl.log(msg);
  }
}

function LogGLCall(functionName, args) {
  if (g_logGLCalls) {
    ValidateNoneOfTheArgsAreUndefined(functionName, args)
    tdl.log("gl." + functionName + "(" +
            tdl.webgl.glFunctionArgsToString(functionName, args) + ")");
  }
}

/**
 * Sets up plane
 */
function setupPlane() {
  var texture = tdl.textures.loadTexture([0,100,200,255]);
  g_videoTexture = texture;
  var textures = {
     texture: texture
  };
  var program = tdl.programs.loadProgramFromScriptTags(
      'basicVertexShader',
      'basicFragmentShader');
  var arrays = tdl.primitives.createPlane(1, 1, 1, 1);
  tdl.primitives.reorient(arrays,
      [1, 0, 0, 0,
       0, 0, 1, 0,
       0, -1, 0, 0,
       0, 0, 0, 1]);
  delete arrays.normal;
  return new tdl.models.Model(program, arrays, textures);
}

/**
 * Sets up RenderTarget plane
 */
function setupRTPlane(rtTexture) {
  var textures = {
     texture: rtTexture
  };
  var program = tdl.programs.loadProgramFromScriptTags(
      'basicVertexShader',
      'basicFragmentShader');
  var arrays = tdl.primitives.createPlane(1, 1, 1, 1);
  tdl.primitives.reorient(arrays,
      [1, 0, 0, 0,
       0, 0, 1, 0,
       0, -1, 0, 0,
       0.5, 0.5, 0, 1]);
  delete arrays.normal;
  return new tdl.models.Model(program, arrays, textures);
}

/**
 * Sets up colorAdjust Plane
 */
function setupColorAdjustPlane(inTexture) {
  g_colorCubeTexture0 = tdl.textures.loadTexture(g_adjustmentImages[0].img);
  g_colorCubeTexture1 = tdl.textures.loadTexture(g_adjustmentImages[1].img);

  g_colorCubeTexture0.setParameter(gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  g_colorCubeTexture0.setParameter(gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  g_colorCubeTexture0.setParameter(gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  g_colorCubeTexture0.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR);

  g_colorCubeTexture1.setParameter(gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  g_colorCubeTexture1.setParameter(gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  g_colorCubeTexture1.setParameter(gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  g_colorCubeTexture1.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR);

  var textures = {
    inTexture: inTexture,
    colorCube0: g_colorCubeTexture0,
    colorCube1: g_colorCubeTexture1
  };

  var arrays = tdl.primitives.createPlane(2, 2, 1, 1);
  tdl.primitives.reorient(arrays,
      [1, 0, 0, 0,
       0, 0,-1, 0,
       0, 1, 0, 0,
       0, 0, 0, 1]);
  delete arrays.normal;

  const models = [];
  for (var i = 0; i < 2; ++i) {
      var program = tdl.programs.loadProgram(
          document.querySelector('#colorAdjustVertexShader').text,
          (i ? '#define USE_NEAREST 1\n' : '') +
          document.querySelector('#colorAdjustFragmentShader').text);
      models.push(new tdl.models.Model(program, arrays, textures));
  }
  return models;
}

function initialize() {
  math = tdl.math;
  fast = tdl.fast;
  canvas = document.getElementById("canvas");

  gl = tdl.webgl.setupWebGL(canvas);
  if (!gl) {
    return false;
  }
  if (g_debug) {
    gl = tdl.webgl.makeDebugContext(gl, undefined, LogGLCall);
  }

  setupAdjustments();
}

function makeSimulatedVolumeImg(name, img, size, callback) {
  var canvas = document.createElement("canvas");
  canvas.width = size * size;
  canvas.height = size;
  var ctx = canvas.getContext("2d");
  ctx.drawImage(img, 0, 0);
  var newImg = new Image();
  newImg.onload = callback;
  newImg.src = canvas.toDataURL();
  return newImg;
}

function updateSelection(index) {
  function updateTexture(tex, index) {
    var info = g_adjustmentImages[index];
    g_useNearest = info.nearest;
    gl.bindTexture(gl.TEXTURE_2D, tex);
    gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
    //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, info.img);
    gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, info.img);
  }
  updateTexture(g_colorCubeTexture1.texture, g_oldIndex);
  updateTexture(g_colorCubeTexture0.texture, index);
  g_adjustmentImages[g_oldIndex].div.style.color = "gray";
  g_adjustmentImages[index].div.style.color = "red";
  g_mixTimer = g_mixDuration;
  g_oldIndex = index;
}

function setupAdjustments() {
  tdl.log("setupAdjustments")
  var loadingCount = 1;
  var adjustmentsElement = document.getElementById("adjustments");
  g_adjustmentNames = g_adjustmentImages.map(function(info, ii) {
    ++loadingCount;
    var div = document.createElement("div");
    info.div = div;
    div.innerHTML = info.name;
    div.className = "clickable";
    div.style.color = ii ? "gray" : "red";
    div.onclick = function(index) {
      return function() {
        if (!loadingCount) {
          updateSelection(index);
        }
      }
    }(ii);
    adjustmentsElement.appendChild(div);
    var img = new Image();
    img.src = info.url;
    img.onload = function(img, info) {
      return function() {
        info.img = makeSimulatedVolumeImg(info.name, img, 8, function() {
          --loadingCount;
          if (loadingCount == 0) {
             continueSetup();
          }
        });
      }
    }(img, info);
    return info.name;
  });
  loadingCount--;
}

function parseQuery(s) {
  const q = {};
  s.substring(s.startsWith('?') ? 1 : 0).split('&').forEach(pair => {
    const parts = pair.split('=').map(decodeURIComponent);
    q[parts[0]] = parts[1];
  });
  return q;
}

function continueSetup() {
  var mainFBO = tdl.framebuffers.createFramebuffer(canvas.width, canvas.height, true);
  var videoPlane = setupPlane();
  var colorAdjustPlanes = setupColorAdjustPlane(mainFBO.texture);
  var rtPlane = setupRTPlane(mainFBO.texture);

  var projection = new Float32Array(16);
  var view = new Float32Array(16);
  var identity = new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);
  var world = new Float32Array(16);
  var worldInverse = new Float32Array(16);
  var worldInverseTranspose = new Float32Array(16);
  var viewProjection = new Float32Array(16);
  var worldViewProjection = new Float32Array(16);
  var viewInverse = new Float32Array(16);
  var viewDirectionProjectionInverse = new Float32Array(16);
  var eyePosition = new Float32Array(3);
  var target = new Float32Array(3);
  var up = new Float32Array([0,1,0]);
  var v3t0 = new Float32Array(3);
  var v3t1 = new Float32Array(3);
  var v3t2 = new Float32Array(3);
  var v3t3 = new Float32Array(3);
  var m4t0 = new Float32Array(16);
  var m4t1 = new Float32Array(16);
  var m4t2 = new Float32Array(16);
  var m4t3 = new Float32Array(16);
  var zero4 = new Float32Array(4);
  var one4 = new Float32Array([1,1,1,1]);

  var startTime = 0;  // 196
  var copyVideo = false;
  var setVideo = false;
  var madeVideoTexture = false;
  var audio;
  var video = document.createElement("video");
  video.playsInline = true;
  video.src = "sample-video.mp4";

  {
    // we need to start inside a touch event
    var start = document.querySelector("#start");
    var once = false;

    start.addEventListener('touchstart', onTouch);
    start.addEventListener('mousedown', onTouch);
    function onTouch() {
      if (once) {
        return;
      }
      once = true;
      start.style.display = "none";
      video.addEventListener("playing", function() {
            copyVideo = true;
          }, true);
      video.addEventListener("ended", function() {
            video.currentTime = startTime; video.play();
          }, true);
      video.addEventListener("error", function(e) {
            console.error("could not play video" + e);
          }, true);
      video.volume = 0;
      video.play();
    }
  }

  var showRT = false;
  document.addEventListener('keypress', function(event) {
    if (event.which == 'd'.charCodeAt(0) ||
        event.which == 'D'.charCodeAt(0)) {
      showRT = !showRT;
      tdl.log(showRT);
    }
  });

  const videoPlanePer = {
    worldViewProjection: worldViewProjection
  };

  const rtPlanePer = {
    worldViewProjection: worldViewProjection
  };

  const q = parseQuery(window.location.search);

  if (q.adjustment) {
    const ndx = g_adjustmentNames.indexOf(q.adjustment);
    if (ndx >= 0) {
      updateSelection(ndx);
    }
  }

  var frameCount = 0;
  var then = (new Date()).getTime() * 0.001;
  var clock = 0.0;

  function render() {
    ++frameCount;
    if (!g_drawOnce) {
      requestAnimationFrame(render);
    }
    var now = (new Date()).getTime() * 0.001;
    var elapsedTime = now - then;
    then = now;

    clock += elapsedTime;
    g_eyeClock += elapsedTime * g_eyeSpeed;
    eyePosition[0] = Math.sin(g_eyeClock) * g_eyeRadius;
    eyePosition[1] = g_eyeHeight;
    eyePosition[2] = Math.cos(g_eyeClock) * g_eyeRadius;
    target[0] = Math.sin(g_eyeClock + Math.PI) * g_targetRadius;
    target[1] = g_targetHeight;
    target[2] = Math.cos(g_eyeClock + Math.PI) * g_targetRadius;

    if (video.currentTime > 0 && (copyVideo || setVideo)) {
      if (setVideo) {
        video.currentTime = audio.currentTime;
      }
      gl.bindTexture(gl.TEXTURE_2D, g_videoTexture.texture);
      if (!madeVideoTexture) {
        madeVideoTexture = true;
        gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.BROWSER_DEFAULT_WEBGL);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video);
        g_videoTexture.setParameter(gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        g_videoTexture.setParameter(gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        g_videoTexture.setParameter(gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        g_videoTexture.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR);
      } else {
        gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, video);
      }
    }

    // Draw to main fbo.
    if (g_applyColorAdjust) {
      mainFBO.bind();
    }

    gl.colorMask(true, true, true, true);
    gl.depthMask(true);
    gl.clearColor(0,0,0,1);
    gl.clearDepth(1);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
    gl.enable(gl.DEPTH_TEST);

    fast.matrix4.perspective(
        projection,
        math.degToRad(30),
        canvas.clientWidth / canvas.clientHeight,
        1,
        5000);
    fast.matrix4.lookAt(
        view,
        eyePosition,
        target,
        up);
    fast.matrix4.mul(viewProjection, view, projection);

//      view: view,
//      projection: projection,
//      viewProjection: viewProjection,

    Log("--Draw Plane---------------------------------------");
    videoPlane.drawPrep();
    if (video.videoWidth) {
      // this is a hack since I'm doing this in 3d.
      // meaning I'm drawing to a polygon in 3d and based on
      // where the view is this math just happesn to work.
      var yAspect = 1;
      var xAspect = video.videoWidth / video.videoHeight;
      var width = canvas.clientHeight * xAspect / canvas.clientWidth;
      if (width < 1) {
        yAspect /= width;
        xAspect /= width;
      }
      fast.matrix4.scaling(world, [xAspect, yAspect, 1]);
    } else {
      fast.matrix4.translation(world, [0, 0, 0]);
    }
    fast.matrix4.mul(worldViewProjection, world, viewProjection);
    fast.matrix4.inverse(worldInverse, world);
    fast.matrix4.transpose(worldInverseTranspose, worldInverse);
    videoPlane.draw(videoPlanePer);

    if (g_mixTimer > 0) {
      g_mixTimer = Math.max(0, g_mixTimer - elapsedTime);
      g_mixAmount = g_mixTimer / g_mixDuration;
    }

    if (g_applyColorAdjust) {
      mainFBO.unbind();

      gl.colorMask(true, true, true, true);
      gl.depthMask(true);
      gl.clearColor(0,1,0,1);
      gl.clearDepth(1);
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
      gl.disable(gl.BLEND);
      gl.disable(gl.DEPTH_TEST);

      const colorAdjustPlane = g_useNearest ? colorAdjustPlanes[1] : colorAdjustPlanes[0];

      const filter = g_useNearest ? gl.NEAREST : gl.LINEAR;
      g_colorCubeTexture0.setParameter(gl.TEXTURE_MIN_FILTER, filter);
      g_colorCubeTexture0.setParameter(gl.TEXTURE_MAG_FILTER, filter);
      g_colorCubeTexture1.setParameter(gl.TEXTURE_MIN_FILTER, filter);
      g_colorCubeTexture1.setParameter(gl.TEXTURE_MAG_FILTER, filter);

      colorAdjustPlane.drawPrep();
      colorAdjustPlane.draw({
          mixAmount: g_mixAmount
        });

      if (showRT) {
        var orthoProjection = [
          2 / canvas.width, 0, 0, 0,
          0, -2 / canvas.height, 0, 0,
          0, 0, 1, 0,
          -1 + 1 / canvas.width, 1 - 1 / canvas.height, 0, 1
        ];
        rtPlane.drawPrep();
        if (video.videoWidth) {
          var aspect = canvas.clientWidth / canvas.clientHeight;
          fast.matrix4.scaling(m4t0, [video.videoWidth, video.videoHeight * aspect, 1]);
        } else {
          fast.matrix4.translation(mt40, [0, 0, 0]);
        }
        fast.matrix4.translation(m4t1, [canvas.width - video.videoWidth, 0, 0]);
        fast.matrix4.mul(world, m4t0, m4t1);
        fast.matrix4.mul(worldViewProjection, world, orthoProjection);
        //fast.matrix4.mul(worldViewProjection, world, viewProjection);
        rtPlane.draw(rtPlanePer);
      }
    }

    // Set the alpha to 255.
    gl.colorMask(false, false, false, true);
    gl.clearColor(0,0,0,1);
    gl.clear(gl.COLOR_BUFFER_BIT);

    // turn off logging after 1 frame.
    g_logGLCalls = false;
  }
  render();
  return true;
}

window.onload = initialize;
</script>
</html>



